/*
 * Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef GHOST_EXPERIMENTS_ROCKSDB_REQUEST_H_
#define GHOST_EXPERIMENTS_ROCKSDB_REQUEST_H_

#include "absl/random/random.h"
#include "absl/time/clock.h"
#include "lib/base.h"

namespace ghost_test {

// A synthetic request for RocksDB generated by 'Ingress'.
struct Request {
  struct Get {
    // The entry to access for the Get request.
    uint32_t entry;
  };

  struct Range {
    // The accessed range is [start_entry, start_entry + size).

    // The first entry in the range.
    uint32_t start_entry;
    // The range size.
    uint32_t size;
  };

  // Returns a sample duration from an exponential distribution with a mean
  // duration of 'mean'.
  // This is used to generate a request service time from an exponential
  // distribution (so the request service times follow a lightly-tailed
  // distribution).
  static absl::Duration GetExponentialHandleTime(absl::BitGen& gen,
                                                 absl::Duration mean) {
    int64_t mean_ns = absl::ToInt64Nanoseconds(mean);
    // In the exponential distribution Exp('lambda'), the expected value (i.e.,
    // the mean) is equal to '1 / lambda'. Thus, we need to pass '1 / mean_ns'
    // as 'lambda' to the exponential distribution to have a mean sample value
    // of 'mean_ns'.
    double handle_ns = absl::Exponential<double>(gen, 1.0 / mean_ns);
    return absl::Nanoseconds(handle_ns);
  }

  // Returns true if this is a Get request. Returns false otherwise (i.e., this
  // is a Range query).
  bool IsGet() const { return work.index() == 0; }

  // Returns true if this is a Range query. Returns false otherwise (i.e., this
  // is a Get request).
  bool IsRange() const { return work.index() == 1; }

  // Unique request identifier.
  uint64_t id;

  // When the request was generated.
  absl::Time request_generated;
  // When the request was picked up by the app.
  absl::Time request_received;
  // When the request was assigned to a worker.
  absl::Time request_assigned;
  // When the request started to be handled by a worker.
  absl::Time request_start;
  // When the worker finished handling the request.
  absl::Time request_finished;

  // The work to do. The request is either a Get request or a Range query.
  std::variant<Get, Range> work;
};

}  // namespace ghost_test

#endif  // GHOST_EXPERIMENTS_ROCKSDB_REQUEST_H_
